#ifndef xpack_lang_cxx_make_json_string
#define xpack_lang_cxx_make_json_string
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::lang_cxx::inc
#include"define/base_type.hpp"
#include"define/classify.hpp"
#include"interface/seqptr.hpp"
#include"interface/unified_seq.hpp"
#include"lang/cxx/strlize.hpp"
#include"lang/cxx.hpp"
#include"macro/xtypeid.hpp"
#include"macro/xunlikely.hpp"
#include"meta/is_class.hpp"
#include"meta/is_origin_arrayx.hpp"
#include"meta/is_ptr.hpp"
#include"meta/is_same.hpp"
#include"meta/is_string_wrapper.hpp"
#include"meta/item_of.hpp"
#include"meta/origin_of.hpp"
#include"meta/remove_ref.hpp"
#include"utils/mix_buffer.hpp"
#pragma pop_macro("xuser")
#define xl_padding              (*(cxx<item_t> *)*l_padding)
#define xpush_str(str)                              \
    if constexpr (sizeof(item_t) == 1){             \
        stream.output(str, sizeof(str) - 1);        \
    }                                               \
    else{                                           \
        stream.output(u"" str, sizeof(str) - 1);    \
    }

namespace mixc::lang_cxx{
    template<class remove_ref_t, class item_t>
    concept is_stringx = 
        inc::is_origin_arrayx<remove_ref_t, item_t> or 
        inc::is_same<item_t const *, remove_ref_t> or
        inc::is_string_wrapper<remove_ref_t>;

    template<class type_t>
    concept is_basic_type_or_warpper = 
        inc::is_class<type_t> == false or (
            inc::is_secret_type<type_t> and 
            inc::is_class<typename inc::origin_of<type_t>> == false
        );

    constexpr make_json_string_t    make_json_string(true);
    constexpr make_json_string_t    make_json_string_non_secret(false); // 和 l_is_secret 匹配

    template<class type_t>
    constexpr const type_t *        padding_white_space{};

    template<class type_t>
    constexpr const type_t *        padding_table{};

    template<> inline constexpr asciis  padding_white_space<char> = "    ";
    template<> inline constexpr words   padding_white_space<char16_t> = u"    ";
    template<> inline constexpr asciis  padding_table<char> = "\t";
    template<> inline constexpr words   padding_table<char16_t> = u"\t";

    xthread_local(l_padding, u08[sizeof(inc::c08)]);
    xthread_local(l_pad_count, uxx);

    template<class item_t>
    inline void padding(auto & stream) {
        if (not xl_padding->is_empty){
            stream.output(item_t('\n'));
            for(uxx i = 0; i < *l_pad_count; i++){
                stream.output((item_t *)xl_padding, xl_padding->length);
            }
        }
    }

    template<class item_t, class object_t, class the_t = cxx<item_t>>
    void make_value(auto & stream, object_t && value, inc::typeid_info info);

    template<class item_t, class object_t, class the_t = cxx<item_t>>
    void make_object(auto & stream, object_t & object);

    template<class item_t, class object_t, class the_t = cxx<item_t>>
    void make_item(auto & stream, object_t & value, inc::typeid_info info);

    template<class item_t, class object_t, class the_t = cxx<item_t>>
    void make_array(auto & stream, object_t & value);

    template<class item_t, class object_t, class the_t = cxx<item_t>>
    inline void make_json_string_core(auto & stream, object_t const & object, auto const & pad){
        xl_padding                  = pad;

        using remove_ref_t          = inc::remove_ref<object_t>;
        constexpr bool is_array     = inc::can_unified_seqlize<remove_ref_t>;

        if constexpr (is_array){
            make_array<item_t>(stream, (object_t &)object);
        }
        else{
            stream.output(item_t('{'));
            *l_pad_count                = 1;
            make_object<item_t>(stream, (object_t &)object);
            *l_pad_count                = 0;
            padding<item_t>(stream);
            stream.output(item_t('}'));
        }
    }

    template<class item_t, class object_t>
    inline void make_value_core(auto & stream, object_t && value){
        // 开启内存检查后出现问题 -lasan -fsanitize=address
        // 原先这块逻辑作为 lambda 放在 make_value() 中，但会出现内存访问异常(可能是误报，也可能是编译器生成的代码不正确)
        // 猜测：可能不能将带捕获参数的 lambda 放在递归函数中
        cxx<item_t>(value, stream);
    };

    template<class item_t, class object_t, class the_t>
    inline void make_value(auto & stream, object_t && value, inc::typeid_info info){
        using remove_ref_t          = inc::remove_ref<decltype(value)>;
        constexpr bool is_string    = is_stringx<remove_ref_t, item_t>;
        constexpr bool is_ptr       = inc::is_ptr<remove_ref_t>;
        bool is_null                = false;

        if constexpr (is_string){
            if constexpr (is_ptr){
                is_null             = value == nullptr;
            }

            if (not is_null){
                auto str            = the_t(value);
                stream.output(item_t('\"'));
                stream.output((item_t *)str, str->length);
                stream.output(item_t('\"'));
            }
        }
        else if constexpr(not is_ptr){
            make_value_core<item_t>(stream, xforward(value));
        }
        // 是指针
        else if (value != nullptr){
            make_item<item_t>(stream, *value, info);

            // make_item 会自己补上结尾的逗号
            // 所以直接返回
            return;
        }
        else{
            is_null                 = true;
        }

        if (is_null){
            xpush_str("null");
        }

        if (not info.is_last_argument){
            stream.output(item_t(','));
        }
    }

    template<class item_t, class object_t, class the_t>
    inline void make_array(auto & stream, object_t & value){
        using remove_ref_t              = inc::remove_ref<object_t>;
        using sub_item_t                = inc::remove_ref<inc::item_of<remove_ref_t>>;
        constexpr auto is_string        = is_stringx<sub_item_t, item_t>;
        constexpr auto is_sub_ary       = inc::can_unified_seqlize<sub_item_t>;
        auto seq                        = inc::unified_seq<remove_ref_t>(value);
        auto seq_length                 = uxx(seq->length);
        *l_pad_count                   += 1;
        stream.output(item_t('['));

        seq.foreach([&](uxx i, auto & item){
            padding<item_t>(stream);
            auto info                   = inc::typeid_info{ .index = i, .is_last_argument = i + 1 == seq_length };

            if constexpr (is_string or (is_basic_type_or_warpper<sub_item_t> and not is_sub_ary/*可能是原始多维数组*/)){
                make_value<item_t>(stream, item, info);
            }
            else{
                make_item<item_t>(stream, item, info);
            }
        });

        *l_pad_count                   -= 1;
        padding<item_t>(stream);
        stream.output(item_t(']'));
    }

    template<class item_t, class object_t, class the_t>
    inline void make_object(auto & stream, object_t & object){
        // 如果你不想考虑太多，推荐使用 xtypeid(object_t).foreach_fields_skip_secret()，少一份泄漏数据的可能性
        // 这里选择考虑太多的方式
        xtypeid(object_t).foreach_fields(object, [&](the_t const & name, auto & value, inc::typeid_info info){
            padding<item_t>(stream);
            stream.output(item_t('\"'));
            stream.output((item_t *)name, name->length);

            xpush_str("\":");
            make_item<item_t>(stream, value, info);
        });
    }

    template<class item_t, class object_t, class the_t>
    inline void make_item(auto & stream, object_t & value, inc::typeid_info info){
        using remove_ref_t              = inc::remove_ref<object_t>;
        constexpr bool is_string        = is_stringx<remove_ref_t, item_t>;
        constexpr bool is_array         = inc::can_unified_seqlize<remove_ref_t>;

        // 此模块内部识别了 secret_type 类型，所以可以使用 xtypeid(object_t).foreach_fields_non_secret()
        // 但效果和 xtypeid(object_t).foreach_fields() 还是有差别
        if constexpr (inc::is_secret_type<remove_ref_t>){
            if (*inner::l_is_secret){
                inc::cxx<item_t> secret(::secret_mark);
                stream.output((item_t *)secret, secret->length);
                // return; 需要带上逗号
            }
            else{
                make_value<item_t>(stream, (typename remove_ref_t::origin_type &)value, info);
                return; // 同下
            }
        }
        // 排到前面
        else if constexpr (is_string){
            make_value<item_t>(stream, value, info);

            // make_value 会自己补上结尾的逗号
            // 所以直接返回
            return;
        }
        else if constexpr (is_array){
            make_array<item_t>(stream, value);
        }
        else if constexpr (is_basic_type_or_warpper<remove_ref_t>){
            make_value<item_t>(stream, value, info);

            // make_value 会自己补上结尾的逗号
            // 所以直接返回
            return;
        }
        else{
            stream.output(item_t('{'));
            *l_pad_count               += 1;
            make_object<item_t>(stream, value);
            *l_pad_count               -= 1;
            padding<item_t>(stream);
            stream.output(item_t('}'));
        }

        if (not info.is_last_argument){
            stream.output(item_t(','));
        }
    }

    template<class item_t>
    template<class object_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(make_json_string_t mode, object_t const & object, xalloc_t const & alloc) :
        cxx(mode, object, {}, alloc){
    }

    template<class item_t>
    template<class object_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(make_json_string_t mode, object_t const & object, the_cref_t pad, xalloc_t const & alloc) : cxx{}/*m_length = 0*/{
        auto old                        = *inner::l_is_secret;

        // 保密模式，make_json_string/make_json_string_non_secret 两种模式都需要设置 l_is_secret
        // 因为 alloc 回调函数的存在，可能会产生嵌套的格式化
        xdefer{
            *inner::l_is_secret         = old;
        };
        *inner::l_is_secret             = mode == make_json_string;

        auto allocx                     = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));

        if constexpr (the_t::helper::template can_stream<xalloc_t>){
            auto stream                 = allocx(0); // 如果缓冲区时流式的，那么不设置长度
            make_json_string_core<item_t>(stream, object, pad);
        }
        else{
            auto buffer                 = inc::mix_buffer<item_t>{};
            auto output                 = [&](item_t const * content, uxx length){
                buffer.push(content, length);
            };
            auto stream_core            = inc::stream_wrapperx<item_t>(output);
            make_json_string_core<item_t>(stream_core, object, pad);

            auto length                 = buffer->length();
            auto stream                 = allocx(length);

            if (stream != nullptr){
                buffer.foreach_level([&](item_t const * buf, uxx length){
                    stream.output(buf, length);
                });
            }
        }
    }
}

#undef  xl_padding
#undef  xpush_str
#endif

xexport(mixc::lang_cxx::make_json_string)
xexport(mixc::lang_cxx::make_json_string_non_secret)
xexport(mixc::lang_cxx::padding_white_space)
xexport(mixc::lang_cxx::padding_table)
